home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 5
/
Apprentice-Release5.iso
/
Source Code
/
Libraries
/
Dots & Pixels
/
toolsources
/
Differential main.cp
next >
Wrap
Text File
|
1995-09-29
|
34KB
|
1,203 lines
#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <fstream.h>
#include <iostream.h>
#include <strstream.h>
#include <Fonts.h>
#include <Menus.h>
#include <Packages.h>
#include <SegLoad.h>
#include <ToolUtils.h>
#include <TextEdit.h>
#include <GestaltEqu.h>
#include <Windows.h>
#include <QuickDraw.h>
#include <QDOffscreen.h>
#include <Palettes.h>
#include <Resources.h>
#include <Timer.h>
#include <Retrace.h>
#include <Devices.h>
#include "general.h"
#include "application.h"
#include "port.h"
#include "window.h"
#include "depthChange.h"
#include "fullscreen.h"
#include "C_randomizer.h"
#include "stopwatch.h"
#include "macutilities.h"
#include "vretrace.h"
#include "streamutils.h"
#include "outputfile.h"
#include "flowsettings.h"
#include "phaser.h"
#include "dotcollection.h"
#include "screenarea.h"
#include "screendots.h"
#include "flowdots.h"
#ifndef __CONDITIONALMACROS__
#define SelectDialogItemText SelIText
#define GetDialogItem GetDItem
#define GetDialogItemText GetIText
#define SetDialogItemText SetIText
#define SetControlValue SetCtlValue
#define GetControlValue GetCtlValue
#define TETextBox TextBox
#define ShowDialogItem ShowDItem
#define HideDialogItem HideDItem
#endif
//
// use_movingnoise is a hack to allow one to study moving noise
//
#define use_movingnoise
#ifdef use_movingnoise
#define noisedots movingnoisedots
#include "movingnoisedots.h"
#else
#include "noisedots.h"
#endif
//
#define acceleration_factor 5.0
#include "Differential main.h"
int step_once( flowdots &alive, flowdots &kicking,
noisedots &alive_noise, noisedots &kicking_noise,
double d_log, double d_rot, double d_trans, Boolean switched = false);
// void main();
void one_loop( opts *the_opts, flowsettings &alive_settings, flowsettings &kicking_settings);
//
// the default options get passed because some of the options are not settable
// in the dialog. Thus, they do not get passed via the path
// default_options => dialog => options,
// but must be passed directly from the default options to the options.
//
void Dialog2options( const DialogPtr theDialog,
const opts *default_options, opts *the_options);
void options2Dialog( const opts *the_options, DialogPtr theDialog);
void FlipItem( DialogPtr myDialog, short theItem);
short GetItemValue( const DialogPtr myDialog, short theItem);
void SetItemValue( DialogPtr myDialog, short theItem, short value);
long GetItemLong( const DialogPtr myDialog, short theItem);
void SetItemLong( DialogPtr myDialog, short theItem, long theValue);
void UpdateSensitivityItems( DialogPtr theDialog, long allow_modifications);
void showParams( const flowdots &alive, const flowdots &kicking, short xPos);
void showLegends( short xPos);
//
// DrawNumber does for numbers what DrawString does for strings. As a bonus
// it can draw numbers with an implicit decimal point. implicitDecimal
// may be anything between zero and eight, inclusive.
// DrawNumber with a double parameter draws with three digits after the decimal point.
//
void DrawNumber( long theNumber, int implicitDecimal = 0);
void DrawNumber( double theNumber);
int main( int argc, char *argv[])
{
application me;
//
// The background screen is included to force a restore of some
// useful palette when the main dialog is made visible again.
// The stimulus generation really messes up the display, and uses
// an 8-bit fullscreen. By creating a four-bit one here we force the
// desctructor of the stimulus fullscreen to switch back to four-bit
// mode, thus giving us a color table we can live with.
// The alternative of simply creating and immediately disposing of a
// fullscreen of 1, 2, or 4 bits does work, too, but is even more
// awful to watch (it flashes black-white-black-almost white).
// This won't work if the main device doesn't support 4-bit mode, and
// we do not check for that, but there already are plenty of other
// things we do not check for.
// (e.g. 68030+, FPU, color QuickDraw,…)
//
fullscreen background( 4, (unsigned char)false);
Handle theResource = Get1Resource( 'pref', 128);
assert( theResource != 0);
assert( (*theResource) != 0);
opts *default_opts = (opts *)*theResource;
opts *the_opts = new opts;
DialogRecord myDialogRecord;
DialogPtr myDialog =
GetNewDialog( 128, &myDialogRecord, (WindowPtr)-1);
if( myDialog == 0)
{
DebugStr( "\pCould not get the dialog!");
exit( EXIT_FAILURE);
}
short itemHit = 0;
options2Dialog( default_opts, myDialog);
SelectDialogItemText( myDialog, iNumDots1, 0, 32767);
flowsettings alive_settings;
flowsettings kicking_settings;
do
{
#ifdef __MWERKS__
{
depthChange een( 1);
depthChange twee( 2);
depthChange vier( 4);
depthChange acht( 8);
}
#endif
ModalDialog( 0L, &itemHit);
switch( itemHit)
{
case iRun:
Dialog2options( myDialog, default_opts, the_opts);
one_loop( the_opts, alive_settings, kicking_settings);
FlushEvents( everyEvent, 0L);
break;
case iQuit:
break;
case iDefaults:
options2Dialog( default_opts, myDialog);
SelectDialogItemText( myDialog, iNumDots1, 0, 32767);
break;
case iNumDots1:
case iNumDots2:
case iNumNoiseDots1:
case iNumNoiseDots2:
case iLife1:
case iLife2:
case iNoiseLife1:
case iNoiseLife2:
case iDiffusion1:
case iDiffusion2:
case iSwitchInterval:
case i_dLog:
case i_dTrans:
case i_dRotDir:
DebugStr( "\pThat item should not be enabled");
break;
case iTransparant:
case iAnnulus:
case iHalves:
case iQuarts:
case iEighths:
case iCheckers8:
case iCheckers16:
case iSurprise:
{
short itemType;
Handle itemHandle;
Rect itemBox;
for( int itemNo = iTransparant; itemNo <= iSurprise; itemNo++)
{
GetDialogItem( myDialog, itemNo,
&itemType, &itemHandle, &itemBox);
SetControlValue( (ControlHandle)itemHandle, (itemNo == itemHit));
}
}
break;
case iFixationDot:
case iLogClicks:
case iCircularMask:
case iFlickering:
case iStepThrough:
FlipItem( myDialog, itemHit);
break;
case i128x128:
case i256x256:
case i512x512:
case i1024x1024:
{
short itemType;
Handle itemHandle;
Rect itemBox;
for( int itemNo = i128x128; itemNo <= i1024x1024; itemNo++)
{
GetDialogItem( myDialog, itemNo,
&itemType, &itemHandle, &itemBox);
SetControlValue( (ControlHandle)itemHandle, (itemNo == itemHit));
}
}
break;
case iAllowModifications:
FlipItem( myDialog, itemHit);
UpdateSensitivityItems( myDialog,
GetItemValue( myDialog, iAllowModifications));
break;
default:
DebugStr( "\pHit an item I haven't been told to exist!");
}
} while( itemHit != iQuit);
ReleaseResource( theResource);
CloseDialog( myDialog);
delete the_opts;
return EXIT_SUCCESS;
}
void one_loop( opts *the_opts, flowsettings &alive_settings, flowsettings &kicking_settings)
{
//
// 'clut' 100 contains 256 entries and is divided into four equal parts
// which all start with black.
//
// 0- 63 contains a pattern which is so that '64' stays black when adding
// up to seven times '8', and becomes white when adding '1' up to seven
// times, and adding '1' dominates adding '8'.
//
// 64-127 contains a pattern which is so that '64' stays black when adding
// up to seven times '1', and becomes white when adding '8' up to seven
// times, and adding '8' dominates adding '1'.
//
// 128-191 contains one black and 63 white entries. Thus, adding up to seven
// times '8' and/or up to seven times '1' makes the pattern white.
//
// 192-254 contains 64 black entries. => '0' stays black when adding
// 1 and/or 8, each up to seven times.
//
// 255 is a special gray value, used to make legends less obnoxious
//
// We draw our pattern using the four indices 0, 64, 128, and 192.
//
// 0 = stimulus one visible
// 64 = stimulus two visible
// 128 = both stimuli visible
// 192 = always black
//
CTabHandle theColorTable = (CTabHandle)Get1Resource( 'clut', 100);
fullscreen thescreen( 8, theColorTable, (unsigned char)true);
Rect screenrect;
thescreen.get_rect( &screenrect);
const int screenHeight = screenrect.bottom - screenrect.top;
const int screenWidth = screenrect.right - screenrect.left;
HideCursor();
const int numdots_1 = (const int)the_opts->numdots_1;
const int numdots_2 = (const int)the_opts->numdots_2;
const int num_noise_1 = (const int)the_opts->num_noise_1;
const int num_noise_2 = (const int)the_opts->num_noise_2;
const int lifetime_1 = (const int)the_opts->lifetime_1;
const int lifetime_2 = (const int)the_opts->lifetime_2;
const int noise_life_1 = (const int)the_opts->noise_life_1;
const int noise_life_2 = (const int)the_opts->noise_life_2;
const int noise_diffusion_1 = (const int)the_opts->diffusion_1;
const int noise_diffusion_2 = (const int)the_opts->diffusion_2;
const int switch_interval = (const int)the_opts->switchinterval;
const int stimType = the_opts->stim_type + iTransparant;
const int numBits = the_opts->resolution_type;
const int do_output = (the_opts->create_output != 0);
const int fixation_dot = (the_opts->fixation_dot != 0);
const int round_frame = (the_opts->round_frame != 0);
const int flickering = (the_opts->flickering != 0);
const double d_log = ((double)the_opts->d_log) / 1000.0;
const double d_rot = ((double)the_opts->d_rot_dir) / 1000.0;
const double d_trans = ((double)the_opts->d_trans) / 1000.0;
const int num_timingloops = the_opts->numtimingloops;
const Boolean stepping = the_opts->stepping;
ofstream *outfile = 0;
if( do_output)
{
//
// 941118: outputfile::outputfile creates a Macintosh file, but doesn't open
// it. This way we can have the best of both worlds, with outputfile::outputfile
// setting creator and type of the file, and yet we can use iostream functions
// to format output.
//
outputfile outfileName( "Differential out");
outfile =
#ifdef __MWERKS__
new ofstream( outfileName(), ios::app | ios::out);
#else
new ofstream( outfileName(), ios::translated | ios::app | ios::out);
#endif
}
const int size = (1 << numBits);
const int half_size = size / 2;
const int half_screenHeight = screenHeight / 2;
const int half_screenWidth = screenWidth / 2;
PicHandle the_pict = (PicHandle)Get1Resource( 'PICT', 128);
const short pict_height = (*the_pict)->picFrame.bottom - (*the_pict)->picFrame.top;
const short pict_width = (*the_pict)->picFrame.right - (*the_pict)->picFrame.left;
const int xpos = max( 0, half_screenWidth - half_size - pict_width);
const int ypos = half_screenHeight - half_size;
flowdots alive( numBits, xpos, ypos, numdots_1, lifetime_1);
flowdots kicking( numBits, xpos, ypos, numdots_2, lifetime_2);
alive.setsettings( alive_settings);
kicking.setsettings( kicking_settings);
#ifdef use_movingnoise
movingnoisedots alive_noise( numBits, xpos, ypos,
num_noise_1, noise_life_1, noise_diffusion_1);
movingnoisedots kicking_noise( numBits, xpos, ypos,
num_noise_2, noise_life_2, noise_diffusion_2);
#else
noisedots alive_noise( numBits, xpos, ypos, num_noise_1, noise_life_1);
noisedots kicking_noise( numBits, xpos, ypos, num_noise_2, noise_life_2);
#endif
const Rect stimulusrect = { ypos, xpos, ypos + size, xpos + size};
PmForeColor( 192);
PaintRect( &stimulusrect);
if( round_frame)
{
RgnHandle theClip = NewRgn();
OpenRgn();
Rect ovalRect = stimulusrect;
if( ovalRect.top < 0)
{
InsetRect( &ovalRect, -ovalRect.top, -ovalRect.top);
}
FrameOval( &ovalRect);
CloseRgn( theClip);
SetClip( theClip);
DisposeRgn( theClip);
}
switch( stimType)
{
case iTransparant:
PmForeColor( 128);
PaintRect( &stimulusrect);
break;
case iAnnulus:
{
PmForeColor( 0);
PaintRect( &stimulusrect);
Rect ovalrect = stimulusrect;
//
// Note: on a 640x480 screen the circular annulus does not fill half the
// stimulus area since 32 scanlines (512-32) are dropped at the top and
// bottom edges of the screen.
//
const int the_inset = (size * 76L) / 512L; // 256 - 256 / √2 ≈ 76
InsetRect( &ovalrect, the_inset, the_inset);
PmForeColor( 64);
PaintOval( &ovalrect);
}
break;
case iHalves:
{
Rect left_half = stimulusrect;
left_half.right = (stimulusrect.left + stimulusrect.right) / 2;
PmForeColor( 64);
PaintRect( &left_half);
Rect right_half = stimulusrect;
right_half.left = left_half.right;
PmForeColor( 0);
PaintRect( &right_half);
}
break;
case iQuarts:
{
Rect a_quart = stimulusrect;
a_quart.right = (stimulusrect.left + stimulusrect.right ) / 2;
a_quart.bottom = (stimulusrect.top + stimulusrect.bottom) / 2;
PmForeColor( 0);
PaintRect( &a_quart);
OffsetRect( &a_quart, half_size, 0);
PmForeColor( 64);
PaintRect( &a_quart);
OffsetRect( &a_quart, 0, half_size);
PmForeColor( 0);
PaintRect( &a_quart);
OffsetRect( &a_quart, -half_size, 0);
PmForeColor( 64);
PaintRect( &a_quart);
}
break;
case iEighths:
{
Rect bigRect = stimulusrect;
InsetRect( &bigRect, -5000, -5000);
int currentcolor = 0;
for( int i = 0; i < 8; i++)
{
PmForeColor( currentcolor);
currentcolor = 64 - currentcolor;
PaintArc( &bigRect, 45 * i, 45);
}
}
break;
case iCheckers8:
case iCheckers16:
{
const int num_checkers = (stimType == iCheckers8) ? 8 : 16;
Rect a_rect = stimulusrect;
const int checker_size = size / num_checkers;
a_rect.right = a_rect.left + checker_size;
a_rect.bottom = a_rect.top + checker_size;
int currentcolor = 0;
for( int y = 0; y < num_checkers; y++)
{
for( int x = 0; x < num_checkers; x++)
{
PmForeColor( currentcolor);
currentcolor = 64 - currentcolor;
PaintRect( &a_rect);
OffsetRect( &a_rect, checker_size, 0);
}
currentcolor = 64 - currentcolor;
OffsetRect( &a_rect, -size, checker_size);
}
}
break;
case iSurprise:
{
PmForeColor( 64);
PaintRect( &stimulusrect);
PmForeColor( 0);
PmBackColor( 64);
TextSize( size / 5);
TextFont( helvetica);
TextStyle( bold);
TextMode( srcCopy);
const char text[] =
"\rWritten by\rReinder Verlinde";
const int length = sizeof( text) - 1; // zero terminator
TETextBox( text, length, &stimulusrect, teJustCenter);
}
break;
default:
DebugStr( "\pUnimplemented stimulus type. This is a bug!");
}
const Rect bigRect = {-32767, -32767, 32767, 32767};
ClipRect( &bigRect);
//
// Add the fixation dot
//
if( fixation_dot)
{
Rect fixationRect = stimulusrect;
PmForeColor( 1);
const short the_inset = half_size - 2; // leave 4x4 pixels
InsetRect( &fixationRect, the_inset, the_inset);
PaintRect( &fixationRect);
}
TextMode( srcCopy);
TextFont( monaco);
//
// Note: TextSizes 9 and 12 don't work (maybe because that font is in ROM,
// or because it is a bitmapped version of Monaco; I don't see why
// either of these should make a difference, but apparently they do)
//
TextSize( 10);
const int params_x = screenWidth - 112; // stimulusrect.right + 16;
PmForeColor( 255);
showLegends( params_x);
//
// Draw the key legends
//
Rect pict_rect;
pict_rect.top = screenHeight - pict_height;
pict_rect.left = screenWidth - pict_width;
pict_rect.bottom = screenHeight;
pict_rect.right = screenWidth;
DrawPicture( the_pict, &pict_rect);
//
// The set-up is completed. Compute the display speed.
//
const double framerate = framerate_of_main_monitor();
const int num_flicker_frames = stepping ? 60 : 1;
vretrace flicker( num_flicker_frames);
stopwatch omega;
int i;
omega.start();
for( i = 0; i < num_timingloops; i++)
{
(void)step_once( alive, kicking,
alive_noise, kicking_noise,
d_log, d_rot, d_trans);
}
const double async_milliseconds =
stopwatch::milliseconds( omega.stop()) / (double)num_timingloops;
flicker.start();
(void)flicker.sync();
omega.start();
for( i = 0; i < num_timingloops; i++)
{
(void)step_once( alive, kicking,
alive_noise, kicking_noise,
d_log, d_rot, d_trans);
(void)flicker.sync();
}
const double sync_milliseconds =
stopwatch::milliseconds( omega.stop()) / (double)num_timingloops;
const int frames_per_update = (int)(sync_milliseconds * framerate / 1000.0 + 0.5);
//
// 941121: OOPS! in real life 'how_often' should not be expressed in the duration
// of the framerate of the monitor, but simply as a frequency, so instead of doing
//
// alive.set_how_often( frames_per_update);
// kicking.set_how_often( frames_per_update);
//
// We have to do
//
alive.set_how_often( 1000.0 / sync_milliseconds);
kicking.set_how_often( 1000.0 / sync_milliseconds);
//
// For the standard settings of this program this makes a difference
// We have:
// sync_milliseconds ≈ 45ms (about three frames)
// frames_per_update = 3
// 1000.0 / sync_milliseconds ≈ 22
//
// This wasn't a real problem since this program has only been used as a demo up till now.
//
if( do_output)
{
(*outfile) << "Two 'flowdots' instances with "
<< numdots_1 << " and " << numdots_2 << " dots\n"
"frame timing without syncing: "
<< async_milliseconds << "ms ("
<< 1000.0 / async_milliseconds << " frames/sec)\n"
"frame timing with syncing: "
<< sync_milliseconds << "ms ("
<< 1000.0 / sync_milliseconds << " frames/sec)\n"
"monitor frame takes : "
<< 1000.0 / framerate << "ms ("
<< framerate << " frames/sec)\n\n"
<< frames_per_update << " Vertical blanks per frame\n"
<< "\nOption settings were:"
<< "\nd_log = " << d_log
<< "\nd_rot = " << d_rot
<< "\nd_trans = " << d_trans
<< "\nstimulus= " << stimType
<< "\nlife 1 = " << lifetime_1
<< "\nlife 2 = " << lifetime_2
<< "\nswitching interval = " << switch_interval
<< "\n\n";
}
int ready = false;
int button_was_down = false;
int current_switch = switch_interval;
Boolean switched = false;
while( !ready)
{
ready = step_once( alive, kicking, alive_noise, kicking_noise, d_log, d_rot, d_trans, switched);
(void)flicker.sync();
if( flickering)
{
alive.EraseDots();
kicking.EraseDots();
(void)flicker.sync();
}
if( switch_interval != 0)
{
current_switch -= 1;
if( current_switch == 0)
{
const flowsettings alive_temp = alive;
const flowsettings kicking_temp = kicking;
alive.setsettings( kicking_temp);
kicking.setsettings( alive_temp);
current_switch = switch_interval;
switched = !switched;
}
}
const int button_is_down = Button();
if( button_is_down && !button_was_down)
{
//
// First show the current flow parameters on the screen
//
if( switched)
{
showParams( kicking, alive, params_x);
} else {
showParams( alive, kicking, params_x);
}
//
// Then also print them to the output file
//
if( do_output)
{
(*outfile)
<< (flowsettings &)alive << '\n'
<< (flowsettings &)kicking
<< "\n\n" << endl;
}
}
button_was_down = button_is_down;
}
//
// remember current settings
//
if( switched)
{
alive_settings = kicking;
kicking_settings = alive;
} else {
alive_settings = alive;
kicking_settings = kicking;
}
ReleaseResource( (Handle)the_pict);
if( do_output)
{
outfile->flush();
outfile->close();
delete outfile;
}
ShowCursor();
ReleaseResource( (Handle)theColorTable);
}
int step_once( flowdots &alive, flowdots &kicking,
noisedots &alive_noise, noisedots &kicking_noise,
double d_log, double d_rot, double d_trans, Boolean switched)
{
alive.make_a_move( 8);
kicking.make_a_move( 1);
alive_noise.make_a_move( 8);
kicking_noise.make_a_move( 1);
//
// Compute new flow parameters:
//
KeyMap thekeys;
GetKeys( thekeys);
const int key_one = thekeys[ 1];
const int caps_is_down = (key_one & 0x00000002) != 0;
const int shift_is_down = (key_one & 0x00000001) != 0;
const int control_is_down = (key_one & 0x00000008) != 0;
const int escape_is_down = (key_one & 0x00002000) != 0;
const int option_is_down = (key_one & 0x00000004) != 0;
const long the_value = thekeys[ 2];
flowdots *theflow = caps_is_down
? (switched ? &alive : &kicking)
: (switched ? &kicking : &alive);
if( control_is_down && !shift_is_down) // finger might slip to control key
{
theflow->expansion = 1.0;
theflow->rotation = 0.0;
theflow->shear_magnitude = 1.0;
theflow->shear_direction = 0.0;
theflow->translation_x = 0.0;
theflow->translation_y = 0.0;
theflow->changed();
} else {
if( the_value != 0)
{
const double scale = shift_is_down ? acceleration_factor : 1.0;
const double power_scale =
shift_is_down ? pow( d_log, acceleration_factor) : d_log;
//
// Note: the following bitmasks are hard-coded for
// the Apple Extended keyboard II's numeric keypad.
//
if( option_is_down)
{
double div = log( theflow->expansion);
double rot = DegreesToRadians( theflow->rotation);
double r;
double fi;
CartesianToPolar( div, rot, &r, &fi);
if( the_value & 0x00080000) r += power_scale - 1.0;
if( the_value & 0x80000000) r -= power_scale - 1.0;
if( the_value & 0x00000200) r = 0.0;
if( r < 0.0)
{
r = 0.0;
}
if( the_value & 0x00000002) fi += DegreesToRadians( scale);
if( the_value & 0x00000010) fi -= DegreesToRadians( scale);
if( the_value & 0x00000008) fi = 0.0;
PolarToCartesian( r, fi, &div, &rot);
theflow->expansion = exp( div);
theflow->rotation = RadiansToDegrees( rot);
} else {
if( the_value & 0x00080000) theflow->expansion *= power_scale;
if( the_value & 0x80000000) theflow->expansion /= power_scale;
if( the_value & 0x00000200) theflow->expansion = 1.00;
if( the_value & 0x00000002) theflow->rotation -= d_rot * scale;
if( the_value & 0x00000010) theflow->rotation += d_rot * scale;
if( the_value & 0x00000008) theflow->rotation = 0.00;
}
if( the_value & 0x00000001) theflow->shear_magnitude *= power_scale;
if( the_value & 0x00004000) theflow->shear_magnitude /= power_scale;
if( the_value & 0x00008000) theflow->shear_magnitude = 1.00;
if( the_value & 0x00000800) theflow->shear_direction -= d_rot * scale;
if( the_value & 0x00002000) theflow->shear_direction += d_rot * scale;
if( the_value & 0x00001000) theflow->shear_direction = 0.00;
if( option_is_down)
{
double dx = theflow->translation_x;
double dy = theflow->translation_y;
double r;
double fi;
CartesianToPolar( dx, dy, &r, &fi);
if( the_value & 0x00000400) r -= d_trans * scale;
if( the_value & 0x00100000) r += d_trans * scale;
if( the_value & 0x02000000) r = 0.0;
if( r < 0.0)
{
r = 0.0;
}
if( the_value & 0x08000000) fi -= DegreesToRadians( scale);
if( the_value & 0x20000000) fi += DegreesToRadians( scale);
if( the_value & 0x00400000) fi = 0.0;
PolarToCartesian( r, fi, &dx, &dy);
theflow->translation_x = dx;
theflow->translation_y = dy;
} else {
if( the_value & 0x00000400) theflow->translation_x -= d_trans * scale;
if( the_value & 0x00100000) theflow->translation_x += d_trans * scale;
if( the_value & 0x02000000) theflow->translation_x = 0;
if( the_value & 0x08000000) theflow->translation_y -= d_trans * scale;
if( the_value & 0x20000000) theflow->translation_y += d_trans * scale;
if( the_value & 0x00400000) theflow->translation_y = 0;
}
theflow->changed();
}
}
return escape_is_down;
}
void FlipItem( DialogPtr myDialog, short theItem)
{
short itemType;
ControlHandle itemHandle;
Rect itemBox;
GetDialogItem( myDialog, theItem, &itemType, (Handle *)&itemHandle, &itemBox);
SetControlValue( itemHandle, 1 - GetControlValue( itemHandle));
}
short GetItemValue( const DialogPtr myDialog, short theItem)
{
short itemType;
ControlHandle itemHandle;
Rect itemBox;
GetDialogItem( myDialog, theItem, &itemType, (Handle *)&itemHandle, &itemBox);
return GetControlValue( itemHandle);
}
void SetItemValue( DialogPtr myDialog, short theItem, short value)
{
short itemType;
ControlHandle itemHandle;
Rect itemBox;
GetDialogItem( myDialog, theItem, &itemType, (Handle *)&itemHandle, &itemBox);
SetControlValue( itemHandle, value);
}
long GetItemLong( const DialogPtr myDialog, short theItem)
{
short itemType;
ControlHandle itemHandle;
Rect itemBox;
GetDialogItem( myDialog, theItem, &itemType, (Handle *)&itemHandle, &itemBox);
Str255 text;
GetDialogItemText( (Handle)itemHandle, text);
long result;
StringToNum( text, &result);
return result;
}
void SetItemLong( DialogPtr myDialog, short theItem, long theValue)
{
short itemType;
ControlHandle itemHandle;
Rect itemBox;
GetDialogItem( myDialog, theItem, &itemType, (Handle *)&itemHandle, &itemBox);
Str255 text;
NumToString( theValue, text);
SetDialogItemText( (Handle)itemHandle, text);
}
void showParams( const flowdots &alive, const flowdots &kicking, short xPos)
{
MoveTo( xPos, 35);
DrawNumber( alive.expansion); DrawNumber( kicking.expansion);
MoveTo( xPos, 75);
DrawNumber( alive.rotation); DrawNumber( kicking.rotation);
MoveTo( xPos, 115);
DrawNumber( alive.shear_magnitude); DrawNumber( kicking.shear_magnitude);
MoveTo( xPos, 155);
DrawNumber( alive.shear_direction); DrawNumber( kicking.shear_direction);
MoveTo( xPos, 195);
DrawNumber( alive.translation_x); DrawNumber( kicking.translation_x);
MoveTo( xPos, 235);
DrawNumber( alive.translation_y); DrawNumber( kicking.translation_y);
}
void showLegends( short xPos)
{
MoveTo( xPos, 20);
DrawString( "\pdiv (/sec)");
MoveTo( xPos, 60);
DrawString( "\pcurl (°/sec)");
MoveTo( xPos, 100);
DrawString( "\pshear (/sec)");
MoveTo( xPos, 140);
DrawString( "\pshear dir (°)");
MoveTo( xPos, 180);
DrawString( "\pTranslation.x");
MoveTo( xPos, 220);
DrawString( "\pTranslation.y");
}
void DrawNumber( double theNumber)
{
DrawNumber( (long)(1000.0 * theNumber), 3);
}
#define help_the_optimizer
void DrawNumber( long theNumber, int implicitDecimal)
{
#define MAXLENGTH 8
assert( implicitDecimal < MAXLENGTH - 3);
unsigned char string[ MAXLENGTH + 1];
string[ 0] = MAXLENGTH;
const unsigned char digits[ 11] = "0123456789";
#ifdef help_the_optimizer
unsigned char *currentptr = &string[ MAXLENGTH];
const unsigned char *currentlimit = &string[ 1];
#else
int currentindex = MAXLENGTH;
#endif
//
// remember the sign (C: the most readable language in the world)
//
const unsigned char zodiac = " -"[ theNumber < 0];
if( theNumber < 0)
{
theNumber = -theNumber;
}
//
// Output all digits up to the decimal point:
//
for( int i = 0; i < implicitDecimal; i++)
{
const int lastdigit = theNumber % 10;
#ifdef help_the_optimizer
*currentptr-- = digits[ lastdigit];
#else
string[ currentindex--] = digits[ lastdigit];
#endif
theNumber /= 10;
}
//
// then output the decimal point, if needed
//
if( implicitDecimal > 0)
{
#ifdef help_the_optimizer
*currentptr-- = '.';
#else
string[ currentindex--] = '.';
#endif
}
//
// Then output all other digits. Output at least one other digit,
// and also check whether we run out of space. We did not have to
// do that above, since 'implicitDecimal < MAXLENGTH - 3' does hold.
//
#ifdef help_the_optimizer
do
{
const int lastdigit = theNumber % 10;
*currentptr-- = digits[ lastdigit];
theNumber /= 10;
} while( (theNumber != 0) && (currentptr > currentlimit));
#else
do
{
const int lastdigit = theNumber % 10;
string[ currentindex--] = digits[ lastdigit];
theNumber /= 10;
} while( (theNumber != 0) && (currentindex > 1));
#endif
//
// Prepend the sign or a character to signal overflow
//
#ifdef help_the_optimizer
if( theNumber == 0)
{
*currentptr-- = zodiac;
} else {
*currentptr-- = '…';
}
#else
if( theNumber == 0)
{
string[ currentindex--] = zodiac;
} else {
string[ currentindex--] = '…';
}
#endif
//
// And prepend spaces for the rest of the string:
//
#ifdef help_the_optimizer
while( currentptr > string)
{
*currentptr-- = ' ';
}
#else
while( currentindex > 0)
{
string[ currentindex--] = ' ';
}
#endif
//
// Finally draw the Pascal string we created:
//
DrawString( string);
}
#ifdef help_the_optimizer
#undef help_the_optimizer
#endif
void Dialog2options( const DialogPtr theDialog,
const opts *default_options, opts *the_options)
{
the_options->numdots_1 = GetItemLong( theDialog, iNumDots1);
the_options->numdots_2 = GetItemLong( theDialog, iNumDots2);
the_options->num_noise_1 = GetItemLong( theDialog, iNumNoiseDots1);
the_options->num_noise_2 = GetItemLong( theDialog, iNumNoiseDots2);
the_options->lifetime_1 = GetItemLong( theDialog, iLife1);
the_options->lifetime_2 = GetItemLong( theDialog, iLife2);
the_options->noise_life_1 = GetItemLong( theDialog, iNoiseLife1);
the_options->noise_life_2 = GetItemLong( theDialog, iNoiseLife2);
the_options->diffusion_1 = GetItemLong( theDialog, iDiffusion1);
the_options->diffusion_2 = GetItemLong( theDialog, iDiffusion2);
the_options->switchinterval = GetItemLong( theDialog, iSwitchInterval);
for( int itemNo = iTransparant; itemNo <= iSurprise; itemNo++)
{
if( GetItemValue( theDialog, itemNo) != 0)
{
the_options->stim_type = itemNo - iTransparant;
}
}
if( GetItemValue( theDialog, i128x128) != 0)
{
the_options->resolution_type = 7;
} else if( GetItemValue( theDialog, i256x256) != 0) {
the_options->resolution_type = 8;
} else if( GetItemValue( theDialog, i512x512) != 0) {
the_options->resolution_type = 9;
} else {
the_options->resolution_type = 10;
}
the_options->create_output = GetItemValue( theDialog, iLogClicks);
the_options->fixation_dot = GetItemValue( theDialog, iFixationDot);
the_options->round_frame = GetItemValue( theDialog, iCircularMask);
the_options->flickering = GetItemValue( theDialog, iFlickering);
the_options->d_log = GetItemLong( theDialog, i_dLog);
the_options->d_rot_dir = GetItemLong( theDialog, i_dRotDir);
the_options->d_trans = GetItemLong( theDialog, i_dTrans);
the_options->allow_modifications
= GetItemValue( theDialog, iAllowModifications);
the_options->stepping = (GetItemValue( theDialog, iStepThrough) != 0);
the_options->numtimingloops = default_options->numtimingloops;
}
void options2Dialog( const opts *the_options, DialogPtr theDialog)
{
SetItemLong( theDialog, iNumDots1, the_options->numdots_1);
SetItemLong( theDialog, iNumDots2, the_options->numdots_2);
SetItemLong( theDialog, iNumNoiseDots1, the_options->num_noise_1);
SetItemLong( theDialog, iNumNoiseDots2, the_options->num_noise_2);
SetItemLong( theDialog, iLife1, the_options->lifetime_1);
SetItemLong( theDialog, iLife2, the_options->lifetime_2);
SetItemLong( theDialog, iNoiseLife1, the_options->noise_life_1);
SetItemLong( theDialog, iNoiseLife2, the_options->noise_life_2);
SetItemLong( theDialog, iDiffusion1, the_options->diffusion_1);
SetItemLong( theDialog, iDiffusion2, the_options->diffusion_2);
SetItemLong( theDialog, iSwitchInterval, the_options->switchinterval);
const int theItemToSet = the_options->stim_type + iTransparant;
for( int itemNo = iTransparant; itemNo <= iSurprise; itemNo++)
{
SetItemValue( theDialog, itemNo, (theItemToSet == itemNo));
}
SetItemValue( theDialog, i128x128, (the_options->resolution_type == 7));
SetItemValue( theDialog, i256x256, (the_options->resolution_type == 8));
SetItemValue( theDialog, i512x512, (the_options->resolution_type == 9));
SetItemValue( theDialog, i1024x1024, (the_options->resolution_type == 10));
SetItemValue( theDialog, iLogClicks, the_options->create_output);
SetItemValue( theDialog, iFixationDot, the_options->fixation_dot);
SetItemValue( theDialog, iCircularMask, the_options->round_frame);
SetItemValue( theDialog, iFlickering, the_options->flickering);
SetItemLong( theDialog, i_dLog, the_options->d_log);
SetItemLong( theDialog, i_dRotDir, the_options->d_rot_dir);
SetItemLong( theDialog, i_dTrans, the_options->d_trans);
SetItemValue( theDialog, iAllowModifications, the_options->allow_modifications);
SetItemValue( theDialog, iStepThrough, the_options->stepping);
UpdateSensitivityItems( theDialog, the_options->allow_modifications);
}
void UpdateSensitivityItems( DialogPtr theDialog, long allow_modifications)
{
//
// this routine should use some kind of table, but it works…
//
if( allow_modifications)
{
ShowDialogItem( theDialog, i_dLog);
ShowDialogItem( theDialog, i_dTrans);
ShowDialogItem( theDialog, i_dRotDir);
HideDialogItem( theDialog, i_dLogStatic);
HideDialogItem( theDialog, i_dTransStatic);
HideDialogItem( theDialog, i_dRotDirStatic);
} else {
HideDialogItem( theDialog, i_dLog);
HideDialogItem( theDialog, i_dTrans);
HideDialogItem( theDialog, i_dRotDir);
SetItemLong( theDialog, i_dLogStatic, GetItemLong( theDialog, i_dLog));
SetItemLong( theDialog, i_dTransStatic, GetItemLong( theDialog, i_dTrans));
SetItemLong( theDialog, i_dRotDirStatic, GetItemLong( theDialog, i_dRotDir));
ShowDialogItem( theDialog, i_dLogStatic);
ShowDialogItem( theDialog, i_dTransStatic);
ShowDialogItem( theDialog, i_dRotDirStatic);
}
}
void PolarToCartesian( double r, double fi, double *x, double *y)
{
*x = r * cos( fi);
*y = r * sin( fi);
}
void CartesianToPolar( double x, double y, double *r, double *fi)
{
//
// 950614: hacking 'hypot' in. It is defined in fp.h, but leads to
// link errors => using a crude macro equivalent
// We don't have to worry about overflow here.
//
#define hypot(a,b) sqrt( a * a + b * b)
*r = hypot( x, y);
*fi = atan2( y, x);
#undef hypot
}